package org.openea.eap.module.obpm.controller.admin.proxy;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sec.etech.security.service.IpUtil;
import com.sec.etech.security.service.PasswordCheckUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.openea.eap.framework.common.enums.CommonStatusEnum;
import org.openea.eap.framework.common.exception.ServiceException;
import org.openea.eap.framework.common.pojo.CommonResult;
import org.openea.eap.framework.common.util.collection.CollectionUtils;
import org.openea.eap.framework.common.util.servlet.ServletUtils;
import org.openea.eap.module.obpm.service.eap.ObpmAuthServiceImpl;
import org.openea.eap.module.obpm.service.obpm.ObmpClientService;
import org.openea.eap.module.obpm.vo.AuthPermissionInfoRespVO;
import org.openea.eap.module.system.controller.admin.auth.vo.AuthLoginReqVO;
import org.openea.eap.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import org.openea.eap.module.system.convert.auth.AuthConvert;
import org.openea.eap.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
import org.openea.eap.module.system.dal.dataobject.permission.MenuDO;
import org.openea.eap.module.system.dal.dataobject.permission.RoleDO;
import org.openea.eap.module.system.dal.dataobject.user.AdminUserDO;
import org.openea.eap.module.system.enums.oauth2.OAuth2ClientConstants;
import org.openea.eap.module.system.service.permission.MenuService;
import org.openea.eap.module.system.service.permission.PermissionService;
import org.openea.eap.module.system.service.permission.RoleService;
import org.openea.eap.module.system.service.user.AdminUserService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static org.openea.eap.framework.common.pojo.CommonResult.error;
import static org.openea.eap.framework.common.pojo.CommonResult.success;
import static org.openea.eap.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

@Tag(name = "管理后台 - 认证")
@RestController
@RequestMapping("/obpm/system/auth")
@Validated
@Slf4j
public class ObpmAuthController {

    @Resource
    private ObpmAuthServiceImpl authService;
    @Resource
    private AdminUserService userService;
    @Resource
    private RoleService roleService;
    @Resource
    private MenuService menuService;
    @Resource
    private PermissionService permissionService;

    @Resource
    private ObmpClientService obmpClientService;

    @PostMapping("/loginByToken")
    @PermitAll
    @Operation(summary = "使用单点登录")
    public CommonResult<JSONObject> loginByToken(@RequestBody com.alibaba.fastjson.JSONObject tokenObject, HttpServletRequest request, HttpServletResponse response) {

        // check dev ? 开发测试环境仅允许特定etech角色

        // 登录方式 默认pc
        String audience = request.getParameter("audience");

        String ip = ServletUtils.getClientIP();
        log.info("Current Login Account is " + ip);

        if (StringUtils.isBlank(ip)) {
            throw new ServiceException(500, "IP未找到");
        }

        // 多次登录失败，IP控制
        // 同IP登录错误13次，冷却IP 20分钟
//        if (StringUtils.isNotBlank(ip) && authService.existInBlackList(ip)) {
//            log.error("IP " + ip + " 密码错误尝试次数过多，请过2小时再尝试");
//            // todo 测试环境测试后再放开
//            //throw new ServiceException(406, "该IP密码错误尝试次数过多，请过2小时再尝试");
//        }

        AuthLoginRespVO respVO = authService.loginByToken(tokenObject, ip);
        JSONObject jsonObject = JSONUtil.parseObj(respVO);
        // 登录cookie
        writeCookie(respVO, request, response);

        Long obpmUserId = 0L;
        JSONObject jsonUser = authService.queryObpmUser(respVO.getUserKey(), false);
        if (!JSONUtil.isNull(jsonUser)) {
            obpmUserId = jsonUser.getLong("id", 0L);
            jsonObject.set("obpmUserId", obpmUserId);
        }

        // log action to etech app
        JSONObject jsonData = new JSONObject();
        if (obpmUserId > 0) {
            jsonData.put("uid", obpmUserId);
        }
        obmpClientService.logAction(respVO.getUserKey(), "login", jsonData);

        return success(jsonObject);
    }

    @PostMapping("/login")
    @PermitAll
    @Operation(summary = "使用账号密码登录")
    public CommonResult<JSONObject> login(@RequestBody AuthLoginReqVO reqVO, HttpServletRequest request, HttpServletResponse response) {

        // check dev ? 开发测试环境仅允许特定etech角色

        // 登录方式 默认pc
        String audience = request.getParameter("audience");

        // 多次登录失败，IP控制
        // 同IP登录错误13次，冷却IP 20分钟
        String userAgent = request.getHeader("User-Agent");
        String account = reqVO.getUsername();
        String ip = ServletUtils.getClientIP();
        log.info("Current Login Account is " + ip);
        if (StringUtils.isNotBlank(ip) && authService.existInBlackList(ip)) {
            log.error("IP " + ip + " 密码错误尝试次数过多，请过2小时再尝试");
            // 测试环境测试后再放开
            //throw new ServiceException(406, "该IP密码错误尝试次数过多，请过2小时再尝试");
        }

        // check password 密码规则检查
        String password = reqVO.getPassword();
        if (PasswordCheckUtil.isSecWeakPassword(password)) {
            throw new ServiceException(405, "密码强度不符合要求，请联系管理员重置密码");
        }

        AuthLoginRespVO respVO = authService.login(reqVO);
        JSONObject jsonObject = JSONUtil.parseObj(respVO);
        // 登录cookie
        writeCookie(respVO, request, response);

        // todo 获取用户ID机制待优化
        Long obpmUserId = 0L;
        JSONObject jsonUser = authService.queryObpmUser(respVO.getUserKey(), false);
        if (!JSONUtil.isNull(jsonUser)) {
            obpmUserId = jsonUser.getLong("id", 0L);
            jsonObject.set("obpmUserId", obpmUserId);
        }
        // 车间图纸预览默认一周，etech默认一天
        // 车间图纸预览若未登录则跳转到单独的图纸预览登录界面，登录完成后再预览图纸
        if ("drawing".equals(audience)) {
            // 复制token对象后更新token并设置过期时间
            int accessTokenValiditySeconds = 60 * 60 * 24 * 7; // 有效期7天
            OAuth2AccessTokenDO drawingTokenDO = authService.createOAuth2CustToken(respVO.getAccessToken(), accessTokenValiditySeconds);
            jsonObject.put("drawingToken", drawingTokenDO.getAccessToken());
            jsonObject.put("drawingTokenExpiry", accessTokenValiditySeconds * 1000);//前端保留有效期 7天 + 14小时
        }

        // log action to etech app
        JSONObject jsonData = new JSONObject();
        if (obpmUserId > 0) {
            jsonData.put("uid", obpmUserId);
        }
        obmpClientService.logAction(respVO.getUserKey(), "login", jsonData);

        return success(jsonObject);
    }

    private void writeCookie(AuthLoginRespVO respVO, HttpServletRequest request, HttpServletResponse response) {
        // 候选方案 兼容eap-bpm 返回cookie
        Cookie cookie = new Cookie("Authorization", "Bearer-" + respVO.getAccessToken());
        String contextPath = request.getContextPath();
        if (ObjectUtils.isEmpty(contextPath)) {
            contextPath = "/";
        }
        // cookie过期时间
        LocalDateTime expiresTime = respVO.getExpiresTime();
        int expire = (int) LocalDateTimeUtil.between(LocalDateTime.now(), expiresTime, ChronoUnit.SECONDS);
        //expire -= 600; // 提前10min
        cookie.setPath(contextPath);
        cookie.setMaxAge(expire);
        response.addCookie(cookie);
    }


    @GetMapping("/get-permission-info")
    @Operation(summary = "获取登录用户的权限信息")
    public CommonResult<AuthPermissionInfoRespVO> getPermissionInfo() {
        // 1.1 获得用户信息
        AdminUserDO user = userService.getUser(getLoginUserId());
        if (user == null) {
            return null;
        }
        // 查询etech用户信息
        JSONObject jsonUser = authService.queryObpmUser(user.getUsername(), false);
        if (JSONUtil.isNull(jsonUser)) {
            jsonUser = new JSONObject();
        }

        // 检查设计管理账号（开发/生产环境）
        int withAdmin = jsonUser.getInt("withAdmin", 0);
        if (withAdmin == 0 && "admin".equals(user.getUsername())) {
            withAdmin = 1;
        }

        // 检查开发环境中是否有权限访问（仅开发环境）
        // 错误提示“对不起，你没有以太客测试服使用权限，如有疑问请联系管理员。”
//        if (!"admin".equals(user.getUsername()) && !"eapadmin".equals(user.getUsername())
//                && jsonUser.containsKey("withTestDev")) {
//            int withTestDev = jsonUser.getInt("withTestDev");
//            if (withTestDev == 0) {
//                return error(409, "对不起，你没有以太客测试服使用权限，如有疑问请联系管理员。");
//            }
//        }

        // 1.2 获得角色列表
        Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
        List<RoleDO> roles = new ArrayList<>();
        if (CollUtil.isNotEmpty(roleIds)) {
            roles = roleService.getRoleList(roleIds);
            roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
        }

        // 1.3 获得菜单列表
//        Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));
//        List<MenuDO> menuList = menuService.getMenuList(menuIds);
//        menuList.removeIf(menu -> !CommonStatusEnum.ENABLE.getStatus().equals(menu.getStatus())); // 移除禁用的菜单
        List<MenuDO> menuList = permissionService.getUserMenuListByUser(user.getId(), user.getUsername());
        // i18n
        // menuList = menuService.toI18n(menuList);


        // 2. 拼接结果返回
        AuthPermissionInfoRespVO authPermissionInfoRespVO = AuthPermissionInfoRespVO.builder()
                .user(AuthPermissionInfoRespVO.UserVO.builder()
                        .id(user.getId())
                        .etechUserId(jsonUser.getLong("id"))
                        .username(user.getUsername())
                        .nickname(user.getNickname())
                        .avatar(user.getAvatar())
                        .userStatus(jsonUser.getJSONObject("userStatus"))
                        .currentOrg(jsonUser.getJSONObject("currentOrg"))
                        .email(jsonUser.getStr("email"))
                        .mobile(jsonUser.getStr("mobile"))
                        .authorities(jsonUser.getJSONArray("authorities"))
                        .build())
                .roles(CollectionUtils.convertSet(roles, RoleDO::getCode))
                .permissions(CollectionUtils.convertSet(menuList, MenuDO::getPermission))
                .obpmPermissions(CollectionUtils.convertSet(menuList, MenuDO::getAlias))
                .menus(AuthConvert.INSTANCE.buildMenuTree(menuList))
                .withAdmin(withAdmin)
                .webHeaderColor(jsonUser.getStr("webHeaderColor"))
                .build();
        return success(authPermissionInfoRespVO);
    }

}
